﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using FlyearAssist.Hibernating.DataModel;
using System.Runtime.Serialization;
using NHibernate.Criterion;
using System.Collections;

namespace LightWeb.Infrastructure.Repositories
{
    public partial class DataContext
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private ISession CurrentSession
        {
            get
            {
                return this.Session;
            }
        }

        #region Read
        #region Pager & NoPager
        /// <summary>
        /// 带分页查询
        /// </summary>
        /// <typeparam name="TModel">要读取的ModelType</typeparam>
        /// <param name="PageIndex">页码</param>
        /// <param name="PageSize">每页数据大小</param>
        /// <param name="WhereCondition">结构化的查询条件</param>
        /// <param name="OrderCondition">结构化的排序条件</param>
        /// /// <param name="doModelInjection">是否做模型注入操作</param>
        /// <returns>包含模型列表,执行结果的一个结构</returns>
        public PagerContent<TModel> Read<TModel>(int PageIndex, int PageSize,
            IList<NCondition> WhereCondition, IList<NOrder> OrderCondition)
        {
            PageInfo pi = new PageInfo();
            pi.EntityType = typeof(TModel);
            pi.PageSize = PageSize;
            pi.PageIndex = PageIndex;

            pi.Conditions = WhereCondition != null ? WhereCondition.ToArray() : null;
            pi.OrderFields = OrderCondition != null ? OrderCondition.ToArray() : null;

            try
            {
                DoPagerQuery(pi);
                return new PagerContent<TModel>(pi.List.OfType<TModel>().ToList(), pi.RecordCount);
            }
            catch (Exception ex)
            {
                log.Error("ModelList ReadByPager Error", ex);
                throw ex;
                //throw new FaultException<ExceptionData>(new ExceptionData(ex.FullMsg()), new FaultReason("GetDataError"));
            }
        }


        public List<TModel> Read<TModel>(List<NCondition> WhereCondition, List<NOrder> OrderCondition)
        {
            QueryInfo qi = new QueryInfo();
            qi.EntityType = typeof(TModel);
            qi.Conditions = WhereCondition != null ? WhereCondition.ToArray() : null;
            qi.OrderFields = OrderCondition != null ? OrderCondition.ToArray() : null;

            try
            {
                DoNoPagerQuery(qi);
                return qi.List.OfType<TModel>().ToList();
            }
            catch (Exception ex)
            {
                log.Error("ModelList ReadNoPager Error", ex);
                //throw new FaultException<ExceptionData>(new ExceptionData(ex.FullMsg()), new FaultReason("GetDataError"));
                throw ex;
            }
        }

        #region 结构化查询
        /// <summary>
        /// 执行带分页查询
        /// </summary>
        /// <param name="pi"></param>
        void DoPagerQuery(PageInfo pi)
        {
            if (string.IsNullOrWhiteSpace(pi.EntityName) && pi.EntityType == null)
            {
                throw new Exception("分页类名不能为空");
            }

            ICriteria qbc = string.IsNullOrWhiteSpace(pi.EntityName)
                ? this.CurrentSession.CreateCriteria(pi.EntityType)
                : this.CurrentSession.CreateCriteria(pi.EntityName);
            //总条数
            qbc.SetProjection(NHibernate.Criterion.Projections.RowCount());
            prepareConditions(qbc, pi.Conditions);
            pi.RecordCount = qbc
                        .SetMaxResults(1)
                        .UniqueResult<int>();
            //总页数
            pi.PageCount =
                pi.RecordCount % pi.PageSize == 0 ?
                pi.RecordCount / pi.PageSize :
                pi.RecordCount / pi.PageSize + 1;

            //qbc.SetProjection(null);
            //分页结果
            ICriteria _qbc = string.IsNullOrWhiteSpace(pi.EntityName)
                ? this.CurrentSession.CreateCriteria(pi.EntityType)
                : this.CurrentSession.CreateCriteria(pi.EntityName);

            prepareConditions(_qbc, pi.Conditions);
            //设置排序
            prepareOrder(_qbc, pi.OrderFields);
            //分页结果
            pi.List = _qbc
                    .SetFirstResult((pi.PageIndex - 1) * pi.PageSize)
                    .SetMaxResults(pi.PageSize)
                    .List();

        }

        /// <summary>
        /// 执行不带分页查询
        /// </summary>
        /// <param name="qi"></param>
        void DoNoPagerQuery(QueryInfo qi)
        {
            if (string.IsNullOrWhiteSpace(qi.EntityName) && qi.EntityType == null)
            {
                throw new Exception("分页类名不能为空");
            }

            //分页结果
            ICriteria _qbc = string.IsNullOrWhiteSpace(qi.EntityName)
                ? this.CurrentSession.CreateCriteria(qi.EntityType)
                : this.CurrentSession.CreateCriteria(qi.EntityName);
            prepareConditions(_qbc, qi.Conditions);
            //设置排序
            prepareOrder(_qbc, qi.OrderFields);
            //分页结果
            qi.List = _qbc.List();
        }

        /// <summary>
        /// 处理条件
        /// </summary>
        /// <param name="qbc"></param>
        /// <param name="conditions"></param>
        void prepareConditions(ICriteria qbc, params NCondition[] conditions)
        {
            if (qbc == null || conditions == null || conditions.Length == 0) return;

            foreach (NCondition condition in conditions)
            {
                if (condition.Operate == Operation.OR)
                {
                    qbc.Add(Restrictions.Or(PreparCriterion(condition.LNC), PreparCriterion(condition.RNC)));
                }
                else
                {
                    #region 别名处理
                    var conditionOldName = condition.PropertyName;
                    if (condition.PropertyName.Contains("."))
                    {
                        var entityType = qbc.GetRootEntityTypeIfAvailable();

                        if (null != entityType)
                        {
                            NHibernate.Type.IType property = null;
                            try
                            {
                                property = this.CurrentSession.SessionFactory.GetClassMetadata(entityType).GetPropertyType(condition.PropertyName);
                            }
                            catch (NHibernate.QueryException) { }
                            //如果当前可以解析,代表当前字段已作映射, 如果 == null, 则视为无法解析,需要加别名
                            if (null == property)
                            {
                                var className = condition.PropertyName.Substring(0, condition.PropertyName.IndexOf('.'));
                                var aliasPath = qbc.GetCriteriaByPath(className);
                                var aliasName = string.Empty;
                                //还未给别名
                                if (null == aliasPath)
                                {
                                    aliasName = "_ea_" + className;
                                    qbc.CreateAlias(className, aliasName);
                                }
                                else
                                {
                                    aliasName = aliasPath.Alias;
                                }
                                condition.PropertyName = condition.PropertyName.Replace(className, aliasName);
                            }
                        }
                    }
                    #endregion

                    #region 构造NHibernate条件
                    switch (condition.Operate)
                    {
                        case Operation.EQ:
                            qbc.Add(Restrictions.Eq(condition.PropertyName, condition.PropertyValue));
                            break;
                        case Operation.GT:
                            qbc.Add(Restrictions.Gt(condition.PropertyName, condition.PropertyValue));
                            break;
                        case Operation.LT:
                            qbc.Add(Restrictions.Lt(condition.PropertyName, condition.PropertyValue));
                            break;
                        case Operation.GE:
                            qbc.Add(Restrictions.Ge(condition.PropertyName, condition.PropertyValue));
                            break;
                        case Operation.LE:
                            qbc.Add(Restrictions.Le(condition.PropertyName, condition.PropertyValue));
                            break;
                        case Operation.ISNULL:
                            qbc.Add(Restrictions.IsNull(condition.PropertyName));
                            break;
                        case Operation.ISNOTNULL:
                            qbc.Add(Restrictions.IsNotNull(condition.PropertyName));
                            break;
                        case Operation.ISEMPTY:
                            qbc.Add(Restrictions.IsEmpty(condition.PropertyName));
                            break;
                        case Operation.ISNOTEMPTY:
                            qbc.Add(Restrictions.IsNotEmpty(condition.PropertyName));
                            break;
                        case Operation.NE:
                            qbc.Add(Restrictions.Not(
                                    Restrictions.Eq(condition.PropertyName, condition.PropertyValue)
                                ));
                            break;
                        case Operation.BETWEEN:
                            if (condition.PropertyValue is Object[])
                            {
                                qbc.Add(Restrictions.Between(
                                    condition.PropertyName,
                                    (condition.PropertyValue as Object[])[0],
                                    (condition.PropertyValue as Object[])[1]
                                  )
                                );
                            }
                            else if (condition.PropertyValue is ICollection)
                            {
                                object[] ary = new object[(condition.PropertyValue as ICollection).Count];
                                (condition.PropertyValue as ICollection).CopyTo(ary, 0);
                                qbc.Add(Restrictions.Between(
                                   condition.PropertyName,
                                   ary[0], ary[1]
                                 )
                               );
                            }
                            break;
                        case Operation.LIKE:

                            qbc.Add(Restrictions.Like(
                                condition.PropertyName,
                                condition.PropertyValue.ToString(),
                                NHibernate.Criterion.MatchMode.Exact
                               )
                             );

                            break;
                    }
                    #endregion

                    #region 恢复condition现场
                    condition.PropertyName = conditionOldName;
                    #endregion
                }
            }
        }

        ICriterion PreparCriterion(NCondition condition)
        {
            switch (condition.Operate)
            {
                case Operation.EQ:
                    return Restrictions.Eq(condition.PropertyName, condition.PropertyValue);

                case Operation.GT:
                    return Restrictions.Gt(condition.PropertyName, condition.PropertyValue);

                case Operation.LT:
                    return Restrictions.Lt(condition.PropertyName, condition.PropertyValue);

                case Operation.GE:
                    return Restrictions.Ge(condition.PropertyName, condition.PropertyValue);

                case Operation.LE:
                    return Restrictions.Le(condition.PropertyName, condition.PropertyValue);

                case Operation.ISNULL:
                    return Restrictions.IsNull(condition.PropertyName);

                case Operation.ISNOTNULL:
                    return Restrictions.IsNotNull(condition.PropertyName);

                case Operation.ISEMPTY:
                    return Restrictions.IsEmpty(condition.PropertyName);

                case Operation.ISNOTEMPTY:
                    return Restrictions.IsNotEmpty(condition.PropertyName);

                case Operation.NE:
                    return Restrictions.Not(
                             Restrictions.Eq(condition.PropertyName, condition.PropertyValue)
                         );

                case Operation.OR:
                    //qbc.Add(Restrictions.Or(Restrictions.IsNull(condition.PropertyName)));
                    return Restrictions.Or(PreparCriterion(condition.LNC), PreparCriterion(condition.RNC));

                case Operation.BETWEEN:
                    return Restrictions.Between(
                        condition.PropertyName,
                        (condition.PropertyValue as Object[])[0],
                        (condition.PropertyValue as Object[])[1]
                      );

                case Operation.LIKE:
                    return Restrictions.Like(
                         condition.PropertyName,
                         condition.PropertyValue.ToString(),
                         NHibernate.Criterion.MatchMode.Exact
                      );
                default:
                    throw new Exception("Where condition property operate can't identify, Current operate is:" + condition.Operate.ToString());
            }
        }

        /// <summary>
        /// 处理排序
        /// </summary>
        /// <param name="qbc"></param>
        /// <param name="orderFields"></param>
        void prepareOrder(ICriteria qbc, params NOrder[] orderFields)
        {
            if (qbc == null || orderFields == null ||
                orderFields.Length == 0)
            {
                return;
            }

            foreach (NOrder order in orderFields)
            {
                qbc.AddOrder(
                    order.OrderType == OrderDirection.ASC ?
                    NHibernate.Criterion.Order.Asc(order.PropertyName) :
                    NHibernate.Criterion.Order.Desc(order.PropertyName)
                );
            }
        }
        #endregion
        #endregion


        #region Simple
        public TModel Read<TModel, TID>(TID oID)
        {
            return this.CurrentSession.Get<TModel>(oID);
        }

        public TModel FindByProperty<TModel>(string propertyName, object propertyValue)
        {
            return
             this.CurrentSession
                 .CreateCriteria(typeof(TModel))
                 .Add(Restrictions.Eq(propertyName, propertyValue))
                 .SetMaxResults(1)
                 .UniqueResult<TModel>();
        }

        public TModel FindByProperty<TModel>(string[] propertyNames, object[] propertyValues)
        {
            if (propertyNames == null ||
                propertyValues == null ||
                propertyNames.Length == 0 ||
                propertyValues.Length == 0 ||
                propertyNames.Length != propertyValues.Length)
            {
                return default(TModel);
            }

            ICriteria qbc = this.CurrentSession.CreateCriteria(typeof(TModel));
            for (int i = 0; i < propertyNames.Length; i++)
            {
                qbc.Add(Restrictions.Eq(propertyNames[i], propertyValues[i]));
            }

            qbc.SetMaxResults(1);
            return qbc.UniqueResult<TModel>();
        }
        #endregion
        #endregion

        #region Persist
        public object Persist(object model)
        {
            return this.CurrentSession.Save(model);
        }
        #endregion

        #region Update
        public DataContextResult Update(object model)
        {
            try
            {
                this.CurrentSession.Update(model);
                return new DataContextResult();
            }
            catch (Exception ex)
            {
                return new DataContextResult(ex);
            }
        }
        #endregion

        #region Delete
        public DataContextResult Delete(object model)
        {
            try
            {
                this.CurrentSession.Delete(model);
                return new DataContextResult();
            }
            catch (Exception ex)
            {
                return new DataContextResult(ex);
            }
        }

        public int Delete(string query)
        {
            return this.Delete(query);
        }

        public int Delete(string query, object value, NHibernate.Type.IType type)
        {
            return this.CurrentSession.Delete(query, value, type);
        }

        public int Delete(string query, object[] values, NHibernate.Type.IType[] types)
        {
            return this.CurrentSession.Delete(query, values, types);
        }
        #endregion
    }

    public class DataContextResult
    {
        public DataContextResult(bool successful = true)
        {
            this.Successful = successful;
        }

        public DataContextResult(string message, bool successful = false)
        {
            this.Successful = successful;
            this.Message = message;
        }

        public DataContextResult(Exception exception, bool successful = false)
        {
            this.Successful = successful;
            this.Message = exception.Message;
            this.Exception = exception;
        }

        public bool Successful { get; private set; }
        public string Message { get; private set; }
        public Exception Exception { get; private set; }
    }

    [DataContract(Namespace = "urn:FlyearAssist.Wcf.MessageModel", Name = "PagerContext_{0}")]
    public class PagerContent<TModel>
    {
        private IList<TModel> _ModelList = null;

        [DataMember(Name = "ModelList")]
        public IList<TModel> ModelList
        {
            get
            {
                if (_ModelList == null) _ModelList = new List<TModel>();
                return _ModelList;
            }
            set { _ModelList = value; }
        }

        [DataMember(Name = "TotalListCount")]
        public int TotalListCount { get; set; }

        public PagerContent()
        {
        }

        public PagerContent(IList<TModel> modelList, int totalListCount)
        {
            ModelList = modelList;
            TotalListCount = totalListCount;
        }
    }
}
